package com.xiaoshuidi.cloud.module.contract.service.contract.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.xiaoshuidi.cloud.framework.common.pojo.CommonResult;
import com.xiaoshuidi.cloud.framework.common.pojo.PageResult;
import com.xiaoshuidi.cloud.module.contract.controller.admin.contract.vo.settlement.ContractSettlementWithDetailRespVO;
import com.xiaoshuidi.cloud.module.contract.controller.admin.contract.vo.stay.*;
import com.xiaoshuidi.cloud.module.contract.convert.contract.ContractStayConvert;
import com.xiaoshuidi.cloud.module.contract.dal.dataobject.contract.*;
import com.xiaoshuidi.cloud.module.contract.dal.mysql.contract.ContractMeterfeesMapper;
import com.xiaoshuidi.cloud.module.contract.dal.mysql.contract.ContractRoomMapper;
import com.xiaoshuidi.cloud.module.contract.dal.mysql.contract.ContractStayMapper;
import com.xiaoshuidi.cloud.module.contract.enums.contract.ContractStatusEnum;
import com.xiaoshuidi.cloud.module.contract.enums.contract.CustomerTypeEnum;
import com.xiaoshuidi.cloud.module.contract.enums.contract.DeliveryStatusEnum;
import com.xiaoshuidi.cloud.module.contract.service.contract.ContractRoomService;
import com.xiaoshuidi.cloud.module.contract.service.contract.ContractSettlementService;
import com.xiaoshuidi.cloud.module.contract.service.contract.ContractStayService;
import com.xiaoshuidi.cloud.module.contract.service.contract.CustomerCompanyService;
import com.xiaoshuidi.cloud.module.contract.service.contract.dto.ContractStayRoomEquipmentDTO;
import com.xiaoshuidi.cloud.module.member.api.message.MemberMessageApi;
import com.xiaoshuidi.cloud.module.rooms.api.RoomEquipmentApi;
import com.xiaoshuidi.cloud.module.rooms.api.roomhouse.RoomHouseApi;
import com.xiaoshuidi.cloud.module.rooms.vo.RoomEquipmentVO;
import com.xiaoshuidi.cloud.module.rooms.vo.RoomHouseRespVo;
import com.xiaoshuidi.cloud.module.system.api.user.AdminUserApi;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.xiaoshuidi.cloud.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.xiaoshuidi.cloud.module.contract.enums.ApiConstants.ZERO;
import static com.xiaoshuidi.cloud.module.contract.enums.ErrorCodeConstants.*;

/**
 * 交房退房 Service 实现类
 *
 * @author 合房租赁
 */
@Slf4j
@Service
public class ContractStayServiceImpl implements ContractStayService {

    @Resource
    private ContractStayMapper stayMapper;
    @Resource
    private ContractMeterfeesMapper meterfeesMapper;
    @Lazy
    @Resource
    private ContractRoomService contractRoomService;
    @Resource
    private CustomerCompanyService customerCompanyService;
    @Lazy
    @Resource
    private ContractSettlementService contractSettlementService;
    @Resource
    private RoomEquipmentApi roomEquipmentApi;
    @Resource
    private MemberMessageApi memberMessageApi;
    @Resource
    private ContractRoomMapper contractRoomMapper;
    @Resource
    private AdminUserApi adminUserApi;
    @Resource
    private RoomHouseApi roomHouseApi;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createStay(ContractStayCreateReqVO createReqVO) {
        // 校验合同交房清单是否存在
        validateContractStayExists(createReqVO.getRoomContractId());
        // 校验合同状态
        validateContractStatus(createReqVO.getRoomContractId());
        // 插入
        ContractStayDO stay = ContractStayConvert.INSTANCE.convert(createReqVO);
        insertStayInfo(stay, createReqVO.getRoomAssetDtoList());
        List<ContractMeterfeesCreateReqVO> meterFeesList = createReqVO.getMeterFeesList();
        if (CollectionUtils.isNotEmpty(meterFeesList)){
            Long roomContractId = stay.getRoomContractId();
            meterFeesList.stream().forEach(m -> m.setRoomContractId(roomContractId));
            List<ContractMeterfeesDO> contractMeterfeesList = ContractStayConvert.INSTANCE.convertList(meterFeesList);
            meterfeesMapper.insertBatch(contractMeterfeesList);
        }
        // 返回
        return stay.getId();
    }

    /**
     * 保存入住清单
     */
    private Long insertStayInfo(ContractStayDO stay, List<ContractStayRoomEquipmentDTO> roomAssetDTO){
        Long roomContractId = stay.getRoomContractId();
        ContractRoomDO contractRoom = contractRoomService.get(roomContractId);
        if (Objects.isNull(contractRoom)){
            throw exception(CONTRACT_NOT_EXISTS);
        }
        // 脏房不能入住
        CommonResult<RoomHouseRespVo> roomHouseinfoById = roomHouseApi.getRoomHouseinfoById(contractRoom.getRoomId());
        if (roomHouseinfoById.isSuccess()) {
            RoomHouseRespVo roomHouse = roomHouseinfoById.getData();
            if (!roomHouse.getIsClean()){
                throw exception(DIRTY_ROOM_UNABLE_CHECK_IN);
            }
        }
        stay.setRoomId(contractRoom.getRoomId());
        stay.setRoomName(contractRoom.getRoomName());
        stay.setApartmentId(contractRoom.getApartmentId());
        stay.setApartmentName(contractRoom.getApartmentName());
        stay.setContractNo(contractRoom.getContractNo());
        stay.setContractType(contractRoom.getCustomerType());
        stay.setDeliverer(contractRoom.getCustomerName());
        stay.setRenterName(contractRoom.getCustomerName());
        // 企业合同承租人 企业名称
        if (CustomerTypeEnum.COMPANY.equals(contractRoom.getCustomerType())){
            CustomerCompanyDO company = customerCompanyService.getCompanyDoById(contractRoom.getCustomerCompanyId());
            if (Objects.nonNull(company)){
                stay.setRenterName(company.getCompanyName());
            }
        }
        // 所有房间资产信息
        if (CollectionUtils.isNotEmpty(roomAssetDTO)){
            Map<Long, ContractStayRoomEquipmentDTO> dtoMap = roomAssetDTO.stream().collect(Collectors.toMap(ContractStayRoomEquipmentDTO::getId, Function.identity(), (key1, key2) -> key2));
            List<ContractStayRoomEquipmentDTO> roomEquipmentsList = getRoomEquipmentsList(contractRoom.getRoomId());
            for (ContractStayRoomEquipmentDTO dto : roomEquipmentsList) {
                if (dtoMap.containsKey(dto.getId())){
                    dto.setRemarks(Objects.nonNull(dtoMap.get(dto.getId()))?dtoMap.get(dto.getId()).getRemarks():"");
                }
            }
            stay.setRoomAsset(JSONObject.toJSONString(roomEquipmentsList));
        }else {
            stay.setRoomAsset(getRoomEquipmentsJson(contractRoom.getRoomId()));
        }
        // 入住交房状态 待租客确认
        stay.setDeliveryStatus(DeliveryStatusEnum.WAIT_RENTER_CONFIRM.getCode());
        stayMapper.insert(stay);
        return stay.getId();
    }

    /**
     * 获得所有房间资产信息
     */
    @Override
    public String getRoomEquipmentsJson(Long roomId) {
        List<ContractStayRoomEquipmentDTO> roomEquipmentsList = getRoomEquipmentsList(roomId);
        if (CollectionUtils.isNotEmpty(roomEquipmentsList)){
            return JSONObject.toJSONString(roomEquipmentsList);
        }
        return "[]";
    }

    private List<ContractStayRoomEquipmentDTO> getRoomEquipmentsList(Long roomId){
        CommonResult<List<RoomEquipmentVO>> roomEquipment = roomEquipmentApi.getRoomEquipmentByRoomId(roomId);
        log.info("保存入住交割清单，查询所有房间资产信息，请求参数：{}，返回参数：{}", roomId, roomEquipment);
        roomEquipment.checkError();
        List<RoomEquipmentVO> roomEquipmentList = roomEquipment.getData();
        if (CollectionUtils.isNotEmpty(roomEquipmentList)){
            List<ContractStayRoomEquipmentDTO> dtoList = ContractStayConvert.INSTANCE.convertRoomEquipments2RoomEquipmentDTOs(roomEquipmentList);
            return dtoList;
        }
        return null;
    }

    /**
     * 合同编号校验合同交房清单是否存在
     */
    private void validateContractStayExists(Long contractId) {
        if (Objects.isNull(contractId)){
            throw exception(CREATE_STAY_MISS_CONTRACT_ID);
        }
        LambdaQueryWrapper<ContractStayDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ContractStayDO::getRoomContractId, contractId);
        List<ContractStayDO> contractStayDOS = stayMapper.selectList(queryWrapper);
        if (CollectionUtils.isNotEmpty(contractStayDOS)){
            throw exception(CONTRACT_STAY_EXISTS);
        }
    }

    /**
     * 校验合同状态
     */
    private void validateContractStatus(Long contractId) {
        if (Objects.isNull(contractId)){
            throw exception(CREATE_STAY_MISS_CONTRACT_ID);
        }
        ContractRoomDO contractRoomDO = contractRoomService.get(contractId);
        if (Objects.isNull(contractRoomDO)){
            throw exception(CONTRACT_DOES_NOT_EXISTS);
        }
        List<String> statusList = Arrays.asList(ContractStatusEnum.ACTIVE.getCode(), ContractStatusEnum.CHECK_IN.getCode());
        if (!statusList.contains(contractRoomDO.getContractStatus())){
            throw exception(CONTRACT_STATUS_CANNOT_CREATE_STAY);
        }
    }

    @Override
    public ContractStayRespVO getStay(Long contractId) {
        LambdaQueryWrapper<ContractStayDO> stayQueryWrapper = new LambdaQueryWrapper<>();
        stayQueryWrapper.eq(ContractStayDO::getRoomContractId, contractId);
        stayQueryWrapper.orderByDesc(ContractStayDO::getCreateTime);
        List<ContractStayDO> contractStayDOS = stayMapper.selectList(stayQueryWrapper);
        if (CollectionUtils.isNotEmpty(contractStayDOS)){
            ContractStayDO contractStay = contractStayDOS.get(ZERO);
            ContractStayRespVO respVO = ContractStayConvert.INSTANCE.convert(contractStay);

            Long roomContractId = contractStay.getRoomContractId();
            LambdaQueryWrapper<ContractMeterfeesDO> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(ContractMeterfeesDO::getRoomContractId, roomContractId);
            List<ContractMeterfeesDO> contractMeterfeesList = meterfeesMapper.selectList(queryWrapper);
            if (CollectionUtils.isNotEmpty(contractMeterfeesList)){
                respVO.setMeterFeesList(contractMeterfeesList);
            }
            respVO.setHouseName(contractStay.getApartmentName() + contractStay.getRoomName());
            return respVO;
        }
        return null;
    }

    @Override
    public void editStay(ContractStayUpdateReqVO updateReqVO) {
        // 校验存在
        ContractStayDO stayDO = validateStayExists(updateReqVO.getId());
        // 更新
        ContractStayDO updateObj = ContractStayConvert.INSTANCE.convert(updateReqVO);
        // 重新拉取房间资产信息
        if (CollectionUtils.isNotEmpty(updateReqVO.getRoomAssetDtoList())){
            Map<Long, ContractStayRoomEquipmentDTO> dtoMap = updateReqVO.getRoomAssetDtoList().stream().collect(Collectors.toMap(ContractStayRoomEquipmentDTO::getId, Function.identity(), (key1, key2) -> key2));
            List<ContractStayRoomEquipmentDTO> roomEquipmentsList = getRoomEquipmentsList(stayDO.getRoomId());
            for (ContractStayRoomEquipmentDTO dto : roomEquipmentsList) {
                if (dtoMap.containsKey(dto.getId())){
                    dto.setRemarks(Objects.nonNull(dtoMap.get(dto.getId()))?dtoMap.get(dto.getId()).getRemarks():"");
                }
            }
            updateObj.setRoomAsset(JSONObject.toJSONString(roomEquipmentsList));
        }else {
            updateObj.setRoomAsset(getRoomEquipmentsJson(stayDO.getRoomId()));
        }
        stayMapper.updateById(updateObj);

        if (Objects.nonNull(updateReqVO.getRoomContractId())){
            // 删除抄表计费数据
            LambdaQueryWrapper<ContractMeterfeesDO> deleteMapper = new LambdaQueryWrapper<>();
            deleteMapper.eq(ContractMeterfeesDO::getRoomContractId, updateReqVO.getRoomContractId());
            meterfeesMapper.delete(deleteMapper);

            List<ContractMeterfeesCreateReqVO> meterFeesList = updateReqVO.getMeterFeesList();
            if (CollectionUtils.isNotEmpty(meterFeesList)){
                Long roomContractId = updateReqVO.getRoomContractId();
                meterFeesList.stream().forEach(m -> m.setRoomContractId(roomContractId));
                List<ContractMeterfeesDO> contractMeterfeesList = ContractStayConvert.INSTANCE.convertList(meterFeesList);
                meterfeesMapper.insertBatch(contractMeterfeesList);
            }
        }
    }

    /**
     * 入住ID校验入住交房是否存在
     */
    private ContractStayDO validateStayExists(Long id) {
        ContractStayDO contractStayDO = stayMapper.selectById(id);
        if (contractStayDO == null) {
            throw exception(STAY_NOT_EXISTS);
        }
        return contractStayDO;
    }


    @Override
    public PageResult<ContractStayPageRespVO> getStayPage(ContractStayPageReqVO pageReqVO) {
        Page<ContractStayDO> page = new Page<>(pageReqVO.getPageNo(),pageReqVO.getPageSize());
        QueryWrapper<ContractStayDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(StringUtils.isNotBlank(pageReqVO.getContractType()), ContractStayDO::getContractType, pageReqVO.getContractType())
                .eq(StringUtils.isNotBlank(pageReqVO.getDeliveryStatus()), ContractStayDO::getDeliveryStatus, pageReqVO.getDeliveryStatus())
                .ge(Objects.nonNull(pageReqVO.getDeliveryDateStart()), ContractStayDO::getDeliveryDate,pageReqVO.getDeliveryDateStart())
                .le(Objects.nonNull(pageReqVO.getDeliveryDateEnd()), ContractStayDO::getDeliveryDate,pageReqVO.getDeliveryDateEnd())
                .ge(Objects.nonNull(pageReqVO.getRentOutTimeStart()), ContractStayDO::getRentOutTime,pageReqVO.getRentOutTimeStart())
                .le(Objects.nonNull(pageReqVO.getRentOutTimeEnd()), ContractStayDO::getRentOutTime,pageReqVO.getRentOutTimeEnd());
        if (StringUtils.isNotBlank(pageReqVO.getKeyword())){
            queryWrapper.and(qw -> qw.like("(CONCAT(apartment_name,room_name))",pageReqVO.getKeyword())
                    .or().lambda().like(ContractStayDO::getRenterName,pageReqVO.getKeyword()));
        }
        queryWrapper.lambda().orderByDesc(ContractStayDO::getId);
        page = stayMapper.selectPage(page, queryWrapper);
        List<ContractStayDO> records = page.getRecords();
        List<ContractStayPageRespVO> pageRespList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(records)){
            for (ContractStayDO record : records) {
                ContractStayPageRespVO contractStayPageResp = ContractStayConvert.INSTANCE.convertBean2PageResp(record);
                contractStayPageResp.setHouseName(record.getApartmentName() + record.getRoomName());
                pageRespList.add(contractStayPageResp);
            }
        }
        return new PageResult<>(pageRespList,  page.getTotal());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void confirmDelivery(Long id) {
        ContractStayDO contractStay = validateStayExists(id);
        LambdaUpdateWrapper<ContractStayDO> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(ContractStayDO::getId, id);
        updateWrapper.set(ContractStayDO::getDeliveryConfirmTime, LocalDateTime.now());
        updateWrapper.set(ContractStayDO::getDeliveryStatus, DeliveryStatusEnum.DELIVERED.getCode());
        stayMapper.update(null, updateWrapper);
        contractRoomService.delivery(contractStay.getRoomContractId());
    }


    @Override
    public ContractRentOutStayRespVO getRentOutDeliveryStay(Long id) {
        // 校验存在
        ContractStayDO contractStayDO = validateStayExists(id);
        ContractSettlementWithDetailRespVO settlement = contractSettlementService.getSettlementWithDetailByContractId(contractStayDO.getRoomContractId());
        if (Objects.isNull(settlement)){
            return null;
        }
        ContractRentOutStayRespVO respVO = ContractStayConvert.INSTANCE.convertBean2RentOutStay(contractStayDO);
        respVO.setEvictionTime(settlement.getEvictionTime());
        List<SettlementDetailDO> detailList = settlement.getDetailList();
        if (CollectionUtils.isNotEmpty(detailList)){
            for (SettlementDetailDO settlementDetailDO : detailList) {
                if (StringUtils.isNotBlank(settlementDetailDO.getExtension())){
                    respVO.setInventoryJson(settlementDetailDO.getExtension());
                }
            }
        }
        return respVO;
    }

    public List<ContractStayDO> getStaysByIds(List<Long> ids){
        if (CollectionUtils.isEmpty(ids)){
            return null;
        }
        return stayMapper.selectBatchIds(ids);
    }

    public List<ContractStayDO> getStaysByContractIds(List<Long> ids){
        if (CollectionUtils.isEmpty(ids)){
            return null;
        }
        return stayMapper.selectList(ContractStayDO::getRoomContractId, ids);
    }


    @Override
    public List<ContractStayDO> getStayByMemberNo(Long memberNo) {
        if (Objects.isNull(memberNo)){
            return null;
        }
        return stayMapper.getStayByMemberNo(memberNo);
    }

    @Override
    public void migrateStayData(List<Long> ids) {
        if (CollectionUtils.isNotEmpty(ids)){
            List<ContractRoomDO> contractRoomDOS = contractRoomMapper.selectList(ContractRoomDO::getId, ids);
            if (CollectionUtils.isNotEmpty(contractRoomDOS)){
                Map<Long, Long> collect = contractRoomDOS.stream().collect(Collectors.toMap(ContractRoomDO::getFromId, ContractRoomDO::getId));
                List<Long> oldIds = contractRoomDOS.stream().map(ContractRoomDO::getFromId).collect(Collectors.toList());
                //查出所有的原合同已交房的数据
                LambdaQueryWrapper<ContractStayDO> qw = new LambdaQueryWrapper<>();
                qw.in(ContractStayDO::getRoomContractId,oldIds)
                        .eq(ContractStayDO::getDeliveryStatus,DeliveryStatusEnum.DELIVERED.getCode());
                List<ContractStayDO> contractStayDOS = stayMapper.selectList(qw);
                if (CollectionUtils.isNotEmpty(contractStayDOS)){
                    //迁移数据
                    List<ContractStayDO> data = contractStayDOS.stream().map(m -> {
                        m.setId(null);
                        m.setRoomContractId(collect.get(m.getRoomContractId()));
                        return m;
                    }).collect(Collectors.toList());
                    stayMapper.insertBatch(data);
                }
            }
        }
    }
}
